---
description: A step-by-step guide on integrating Supabase with your browser extension.
---

# Quickstart with Supabase

## Intro

Supabase is an open-source Firebase alternative.

This quickstart is a simple example showcasing how to use Supabase with Plasmo.

## Prerequisites

- [Supabase](https://supabase.com) Account
- Supabase project

## Initialize a Plasmo project with Supabase

```
pnpm create plasmo --with-supabase
```

## Set up Environment Variables

For Supabase to work, we'll need to define a URL and a KEY.

You can find these by finding them in your Supabase project dashboard:

![](@screenshots/supabase-config.png)

Let's add them in an .env file:

```ini filename=".env"
PLASMO_PUBLIC_SUPABASE_URL="CHANGE ME"
PLASMO_PUBLIC_SUPABASE_KEY="CHANGE ME"
```

## Supabase Store

We need to initialize Supabase, so let's add a file called `core/supabase.ts`:

```ts filename="core/supabase.ts"
import { createClient } from "@supabase/supabase-js"

import { Storage } from "@plasmohq/storage"

const storage = new Storage({
  area: "local"
})

export const supabase = createClient(
  process.env.PLASMO_PUBLIC_SUPABASE_URL,
  process.env.PLASMO_PUBLIC_SUPABASE_KEY,
  {
    auth: {
      storage,
      autoRefreshToken: true,
      persistSession: true,
      detectSessionInUrl: true
    }
  }
)
```

## Adding Redirect URL

When a user signs up, they'll need to confirm their email. To do this, we need to add a redirect URL to our Supabase project.

First, we need to create a consistent ID for our extension for development. When you push to the different web stores, you'll get a different ID. To learn more about how all of this works, check out our blog post on [creating consistent extension IDs](https://www.plasmo.com/blog/posts/how-to-create-a-consistent-id-for-your-chrome-extension)

Go to the [Itero KeyPair Tool](https://itero.plasmo.com/tools/generate-keypairs) to generate your consistent extension ID.

We can store the ID and public key in our `.env` file:

```ini filename=".env"
CRX_ID="Replace with the value of CRX ID from Itero KeyPair tool"
CRX_KEY="Replace with the value of Public Key from Itero KeyPair tool"
```

Then, references the public key in your package.json under the manifest.key value:

```json
"manifest": {
  "host_permissions": [
    "https://*/*"
  ],
  "key": "$CRX_KEY",
}
```

Now we need to make it so that the browser doesn't block access to our options page. To do this, we must add it to our web_accessible_resources in our manifest:

```json
"web_accessible_resources": [
  {
    "resources": [
      "options.html"
    ],
    "matches": [
      "<all_urls>"
    ],
    "extension_ids": [
      "$CRX_ID"
    ]
  }
]
```

Head over to the Supabase console and click on the "Authentication" tab, and click URL Configuration.

![](@screenshots/supabase-redirect.png)

Now add the following URL in your site URL as well as your redirect URL:

```text
chrome-extension://<CRX_ID>/options.html
```

Replace `CRX_ID` with the generated extension ID given to you by Itero KeyPairs tool, or the actual ID given to you by the production web store.

## Integrating with a React component

Now we can write code in our React components utilizing Supabase!

Here's an example of using Supabase in a React component for the extension's options page.

```tsx filename="options.tsx"
import type { Provider, User } from "@supabase/supabase-js"
import { useEffect, useState } from "react"

import { Storage } from "@plasmohq/storage"
import { useStorage } from "@plasmohq/storage/hook"

import { supabase } from "~core/supabase"

function IndexOptions() {
  const [user, setUser] = useStorage<User>({
    key: "user",
    instance: new Storage({
      area: "local"
    })
  })

  const [username, setUsername] = useState("")
  const [password, setPassword] = useState("")

  useEffect(() => {
    async function init() {
      const { data, error } = await supabase.auth.getSession()

      if (error) {
        console.error(error)
        return
      }
      if (!!data.session) {
        setUser(data.session.user)
      }
    }

    init()
  }, [])

  const handleEmailLogin = async (
    type: "LOGIN" | "SIGNUP",
    username: string,
    password: string
  ) => {
    try {
      const {
        error,
        data: { user }
      } =
        type === "LOGIN"
          ? await supabase.auth.signInWithPassword({
              email: username,
              password
            })
          : await supabase.auth.signUp({ email: username, password })

      if (error) {
        alert("Error with auth: " + error.message)
      } else if (!user) {
        alert("Signup successful, confirmation mail should be sent soon!")
      } else {
        setUser(user)
      }
    } catch (error) {
      console.log("error", error)
      alert(error.error_description || error)
    }
  }

  const handleOAuthLogin = async (provider: Provider, scopes = "email") => {
    await supabase.auth.signInWithOAuth({
      provider,
      options: {
        scopes,
        redirectTo: location.href
      }
    })
  }

  return (
    <main
      style={{
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
        width: "100%",
        top: 240,
        position: "relative"
      }}>
      <div
        style={{
          display: "flex",
          flexDirection: "column",
          width: 240,
          justifyContent: "space-between",
          gap: 4.2
        }}>
        {user && (
          <>
            <h3>
              {user.email} - {user.id}
            </h3>
            <button
              onClick={() => {
                supabase.auth.signOut()
                setUser(null)
              }}>
              Logout
            </button>
          </>
        )}
        {!user && (
          <>
            <label>Email</label>
            <input
              type="text"
              placeholder="Your Username"
              value={username}
              onChange={(e) => setUsername(e.target.value)}
            />
            <label>Password</label>
            <input
              type="password"
              placeholder="Your password"
              value={password}
              onChange={(e) => setPassword(e.target.value)}
            />

            <button
              onClick={(e) => {
                handleEmailLogin("SIGNUP", username, password)
              }}>
              Sign up
            </button>
            <button
              onClick={(e) => {
                handleEmailLogin("LOGIN", username, password)
              }}>
              Login
            </button>

            <button
              onClick={(e) => {
                handleOAuthLogin("github")
              }}>
              Sign in with GitHub
            </button>
          </>
        )}
      </div>
    </main>
  )
}

export default IndexOptions
```

## Full Example

To see a complete example, check out [with-supabase](https://github.com/PlasmoHQ/examples/tree/main/with-supabase) in our examples repo!
